/** \file
* \par Subversion URL:
* \$URL: http://planar-rendering.googlecode.com/svn/trunk/engine/src/functions.hpp $
* \version \$Rev: 76 $
* \date    \$Date: 2009-04-20 02:26:00 +0000 (Mon, 20 Apr 2009) $
*/

#pragma once

#include <cmath>

#include "iteratecontext.hpp"

/// <summary>
/// An enumeration describing the complex space to use to generate the fractal.
/// </summary>
enum Space
{
	/// <summary>Quaternion</summary>
	space_quat,
	/// <summary>Co-quaternion</summary>
	space_coquat,
	/// <summary>Hyberbolic quaternion</summary>
	space_hypquat,
	/// <summary>Hypercomplex commutative</summary>
	space_hypcplx
};

/// <summary>
/// Function to take the absolute value of each quaternion vector component.
/// </summary>
class zabscomps
{
public:
	/// <summary>
	/// Apply this function to the iteration context.
	/// </summary>
	/// <typeparam name="Space">The complex space to use.</typeparam>
	/// <param name="ic">The iteratiob context to modify.</param>
	template <Space space>
	inline void perform(IterateContext &ic)
	{
		for (int i = 0; i < 4; i++) ic.Z[i] = fabs(ic.Z[i]);
	}
};

/// <summary>
/// Function to square the quaternion.
/// </summary>
class zpow2
{
public:
	/// <summary>
	/// Apply this function to the iteration context.
	/// </summary>
	/// <typeparam name="Space">The complex space to use.</typeparam>
	/// <param name="ic">The iteratiob context to modify.</param>
	template <Space space>
	inline void perform(IterateContext &ic)
	{
		if (space == space_hypcplx)
		{
			const double
				ab = ic.Z[0]*ic.Z[1],
				ac = ic.Z[0]*ic.Z[2],
				ad = ic.Z[0]*ic.Z[3],
				bc = ic.Z[1]*ic.Z[2],
				bd = ic.Z[1]*ic.Z[3],
				cd = ic.Z[2]*ic.Z[3];
			ic.Z[0] = ic.Zsq[0] - ic.Zsq[1] - ic.Zsq[2] - ic.Zsq[3];
			ic.Z[1] = 2*(ab-cd);
			ic.Z[2] = 2*(ac-bd);
			ic.Z[3] = 2*(ad+bc);
			return;
		}
		const double a2 = 2*ic.Z[0];
		switch (space)
		{
		case space_quat:    ic.Z[0] = ic.Zsq[0] - ic.Zsq[1] - ic.Zsq[2] - ic.Zsq[3]; break;
		case space_coquat:  ic.Z[0] = ic.Zsq[0] - ic.Zsq[1] + ic.Zsq[2] + ic.Zsq[3]; break;
		case space_hypquat: ic.Z[0] = ic.Zsq[0] + ic.Zsq[1] + ic.Zsq[2] + ic.Zsq[3]; break;
		}
		for (int i = 1; i < 4; i++) ic.Z[i] *= a2;
	}
};

/// <summary>
/// Function to take the dth power of the quaternion.
/// </summary>
class zpowd
{
public:
	/// <summary>
	/// Apply this function to the iteration context.
	/// </summary>
	/// <typeparam name="Space">The complex space to use.</typeparam>
	/// <param name="ic">The iteratiob context to modify.</param>
	template <Space space>
	inline void perform(IterateContext &ic)
	{
	}

	/// <summary>The exponent to which Z will be raised.</summary>
	double d;
};

/// <summary>
/// Function to add S to the quaternion.
/// </summary>
class zpluss
{
public:
	/// <summary>
	/// Apply this function to the iteration context.
	/// </summary>
	/// <typeparam name="Space">The complex space to use.</typeparam>
	/// <param name="ic">The iteratiob context to modify.</param>
	template <Space space>
	inline void perform(IterateContext &ic)
	{
		ic.Add();
	}
};

/// <summary>
/// Function to add a constant C to the quaternion.
/// </summary>
class zplusc
{
public:
	/// <summary>
	/// Apply this function to the iteration context.
	/// </summary>
	/// <typeparam name="Space">The complex space to use.</typeparam>
	/// <param name="ic">The iteratiob context to modify.</param>
	template <Space space>
	inline void perform(IterateContext &ic)
	{
		for (int i = 0; i < 4; i++) ic.Z[i] += C[i];
	}

	/// <summary>The C constant quaternion.</summary>
	double C[4];
};
